import _ from 'lodash'
import dayjs from 'dayjs';

import SparkChannel from '../ipc/sparkchannel'
import { ReportType, TableType, DATE_FMT } from './common'

class ChannelProxy {
  constructor(channel) {
    this.channel = channel;

    const cache = {};

    for (const prop in channel) {
      const fn = channel[prop];

      if (typeof fn === 'function' && prop.startsWith("request_")) {
        this[prop] = async (...request) => {
          const parts = [prop];
          for (const p of request) {
            if (_.isObject(p)) {
              parts.push(JSON.stringify(p));
            } else {
              parts.push(p.toString());
            }
          }
          const key = _.join(parts, '-');
          // console.log(key);

          let resp;
          if (key in cache) {
            resp = cache[key];
          } else {
            resp = await fn(...request);
            cache[key] = resp;
          }
          return resp;
        };
      }
    }
  }
}

class Fetcher {
  constructor() {
    this.proxy = null;
  }

  async getProxy() {
    if (this.proxy === null) {
      const channel = await SparkChannel();
      return this.proxy = new ChannelProxy(channel);
    }
    return this.proxy;
  }

  async readReal(fullJson) {
    const channel = await this.getProxy();

    const id = fullJson.id;
    const table_type = TableType.getTableTypeId(fullJson.table_type);
    const resp = await channel.request_get_point_value_real({ id, table_type });
    return resp.data.point_value;
  }

  /**
   * return is
   * [{
          "time": "2023-03-01",
          "value": ""
      }, ...]
   */
  async readSeq(fullJson) {
    const { book_type } = fullJson;
    const channel = await this.getProxy();

    const { id, data_type, diff, begin_delta, end_delta, begin, end, time_range_customed } = fullJson;
    const reportType = ReportType.getByValue(book_type);
    const report_type = reportType.id;
    let t1 = begin, t2 = end;
    if (diff) {
      t1 = t1.add(diff, reportType.timeUnit);
      t2 = t2.add(diff, reportType.timeUnit);
    }
    if (!time_range_customed) {
      if (begin_delta) {
        t1 = t1.add(begin_delta, 'minute');
      }
      if (end_delta) {
        t2 = t2.add(end_delta, 'minute');
      }
    }
    const from = t1.format(DATE_FMT), to = t2.format(DATE_FMT);

    let resp;
    if (book_type === 'DAY') {
      if (data_type === 'PERIOD') {
        resp = await channel.request_get_period_value_seq({ id, from, to });
      } else if (data_type === 'HOUR_DIFF') {
        resp = await channel.request_get_diff_value_seq({ id, from, to, report_type });
      }
    } else if (book_type === 'WEEK' || book_type === 'MONTH') {
      if (data_type === 'DAY_MEAN') {
        resp = await channel.request_get_mean_value_seq({ id, from, to, report_type });
      } else if (data_type === 'DAY_MAX') {
        resp = await channel.request_get_max_value_seq({ id, from, to, report_type });
      } else if (data_type === 'DAY_MIN') {
        resp = await channel.request_get_min_value_seq({ id, from, to, report_type });
      } else if (data_type === 'DAY_DIFF') {
        resp = await channel.request_get_diff_value_seq({ id, from, to, report_type });
      } else if (data_type === 'DAY_LAST') {
        resp = await channel.request_get_last_value_seq({ id, from, to, report_type });
      }
    } else if (book_type === 'YEAR') {
      if (data_type === 'MONTH_MEAN') {
        resp = await channel.request_get_mean_value_seq({ id, from, to, report_type });
      } else if (data_type === 'MONTH_MAX') {
        resp = await channel.request_get_max_value_seq({ id, from, to, report_type });
      } else if (data_type === 'MONTH_MIN') {
        resp = await channel.request_get_min_value_seq({ id, from, to, report_type });
      } else if (data_type === 'MONTH_DIFF') {
        resp = await channel.request_get_diff_value_seq({ id, from, to, report_type });
      } else if (data_type === 'MONTH_LAST') {
        resp = await channel.request_get_last_value_seq({ id, from, to, report_type });
      }
    }

    if (resp) {
      const value_list = resp.data.value_list;
      for (const item of value_list) {
        item.time = dayjs(item.time).toDate();
      }
      return value_list;
    } else {
      throw new Error(`Unprocessed holder in ${book_type} report.`);
    }
  }

  async readStat(fullJson) {
    const { book_type, type } = fullJson;
    const channel = await this.getProxy();

    const { id, data_type, diff, begin_delta, begin, time_range_customed } = fullJson;
    const reportType = ReportType.getByValue(book_type);
    const report_type = reportType.id;
    if (time_range_customed) {
      // 自定义事件范围的话统计填充失效
      return null;
    }
    let t1 = begin;
    // let t2 = end;
    if (diff) {
      t1 = t1.add(diff, reportType.timeUnit);
      // t2 = t2.add(diff, reportType.timeUnit);
    }
    if (begin_delta) {
      t1 = t1.add(begin_delta, 'minute');
    }
    // if (end_delta) {
    //   t2 = t2.add(end_delta, 'minute');
    // }
    const time = t1.format(DATE_FMT);

    function getValue(resp) {
      const v = resp.data.value_list[0];
      if (type === 'STAT') {
        return v && v.value;
      } else if (type === 'STAT_TIME') {
        return v && v.time && dayjs(v.time).toDate();
      }
    }

    if (data_type === 'MIN') {
      const resp = await channel.request_get_min_value_stats({ id, time, report_type });
      return getValue(resp);
    } else if (data_type === 'MAX') {
      const resp = await channel.request_get_max_value_stats({ id, time, report_type });
      return getValue(resp);
    } else if (data_type === 'AVG') {
      const resp = await channel.request_get_mean_value_stats({ id, time, report_type });
      return getValue(resp);
    } else if (data_type === 'ABS_MIN') {
      const resp = await channel.request_get_min_value_abs_stats({ id, time, report_type });
      return getValue(resp);
    } else if (data_type === 'ABS_MAX') {
      const resp = await channel.request_get_max_value_abs_stats({ id, time, report_type });
      return getValue(resp);
    } else if (data_type === 'ABS_AVG') {
      const resp = await channel.request_get_mean_value_abs_stats({ id, time, report_type });
      return getValue(resp);
    } else if (data_type === 'VOLT_ELIG') {
      const resp = await channel.request_get_voltage_eligibility_rate({ id, time, report_type });
      return resp.data.voltage_eligibility_rate;
    } else if (data_type === 'OVER_TIME_HIGH') {
      const resp = await channel.request_get_over_limit_time_h({ id, time, report_type });
      return resp.data.high_limit_time;
    } else if (data_type === 'OVER_TIME_LOW') {
      const resp = await channel.request_get_over_limit_time_l({ id, time, report_type });
      return resp.data.low_limit_time;
    }
    throw new Error(`Unprocessed holder in ${book_type} report.`);
  }
}

export default Fetcher